package com.gaofans.nrpc.client.proxy;

import com.gaofans.nrpc.client.bootstrap.ChannelManager;
import com.gaofans.nrpc.common.id.IdGenerator;
import com.gaofans.nrpc.common.processor.Builder;

import java.lang.reflect.Constructor;
import java.net.URI;
import java.util.Objects;

/**
 * @author GaoFans
 */
public class ServiceProxyBuilder<T> implements Builder<T> {

    private final ChannelManager channelManager;
    private URI uri;
    private Class<T> serviceClass;

    public ServiceProxyBuilder(ChannelManager channelManager) {
        this.channelManager = channelManager;
    }

    public void setUri(URI uri) {
        this.uri = uri;
    }

    public void setServiceClass(Class<T> serviceClass) {
        this.serviceClass = serviceClass;
    }

    @Override
    @SuppressWarnings("unchecked")
    public T build() {
        Objects.requireNonNull(serviceClass);
        Objects.requireNonNull(uri);
        String proxyClassName = serviceClass.getName() + "GenRpcProxy";
        Class<?> proxyClass = loadClass(proxyClassName, serviceClass);
        Constructor<?> constructor;
        Object instance;
        constructor = getConstructor(proxyClass, ChannelManager.class, URI.class,IdGenerator.class);
        instance = createInstance(constructor, channelManager, uri,channelManager.idGenerator());

        return (T) instance;
    }

    private static Class<?> loadClass(String name, Class<?> origin) {
        try {
            return origin.getClassLoader().loadClass(name);
        } catch (ClassNotFoundException e) {
            throw new IllegalStateException("Cannot find proxyClass: " + name, e);
        }
    }

    private static Constructor<?> getConstructor(Class<?> clazz, Class<?>... types) {
        try {
            return clazz.getDeclaredConstructor(types);
        } catch (NoSuchMethodException e) {
            throw new IllegalStateException("Cannot find constructor on: " + clazz.getName(), e);
        }
    }

    private static Object createInstance(Constructor<?> constructor, Object... args) {
        try {
            return constructor.newInstance(args);
        } catch (Exception var3) {
            throw new IllegalStateException("Failed to call constructor on", var3);
        }
    }

}
